#include"interact.h"
//命令实现
void cmd_pwd(int argc,char args[][ARGS_LENGTH],puserStat puser)
{
    //检查参数
    if(argc!=1)
    {
        fprintf(stderr,"err in cmd_pwd :arg number\n");
        return;
    }
    printf("%s\n", puser->pwd);
}

//查看当前目录下的文件：文件种类 文件名 文件大小
void cmd_ls(int argc,char args[][ARGS_LENGTH],puserStat puser)
{
    printf("ls runing...\n");
    int ret = 0;
    //检查参数：
    if(argc != 1)
    {
        fprintf(stderr, "err in cmd_ls :wrong arg number\n");
        return;
    }
    //发送请求报文  ：(cmdtype )+ (dataLen + pwd)
    int cmdtype = CMD_LS;
    train_t sender;
    
    //发送命令类型代号
    ret = send(puser->soecktFd, &cmdtype, sizeof(int), 0);
    if(ret == -1)
    {
        perror("send1 in cmd_ls");
        return;
    }
    printf("ret :%d send cmdtype ok\n", ret);
    //发送当前目录
    sender.dataLen = strlen(puser->pwd);
    strcpy(sender.buf, puser->pwd);
    ret = send(puser->soecktFd, &sender, sizeof(int)+sender.dataLen, 0);
    if(ret == -1)
    {
        perror("send2 in cmd_ls");
        return;
    }
    //接收回复报文
    int fileCounter = 0;
    char buf[1000] = {0};
    int len = 0;
    recvCyc(puser->soecktFd, &len, sizeof(int));
    while(len!=-1)
    {
        fileCounter++;
        recvCyc(puser->soecktFd, buf, len);
        printf("%s\n", buf);
        recvCyc(puser->soecktFd, &len, sizeof(int));
    }
    //打印信息
    printf("\t\t\t\t%d files total\n",fileCounter);
}

//cd 验证参数后 将pwd改变成新目录
//只输入 cd 回家
//输入cd .. 向上一层(根下不允许) 
//输入cd 文件夹名字 向下一层 
//输入cd 从用户根开始的一个路径，跳转到该路径（其中不能包括..）
void cmd_cd(int argc,char args[][ARGS_LENGTH],puserStat puser)
{
     printf("cd runing...\n");
    int ret = 0;
    //检查参数：    1：只有cd 直接回家目录  2：有cd和参数 cd到相应参数位置
    if(argc > 2)
    {
        fprintf(stderr, "err in cmd_ls :arg number\n");
        return;
    }
    if(argc == 1)//cd ：回家
    {
        bzero(&puser->pwd,sizeof(puser->pwd));
        strcpy(puser->pwd, "/");
        return;
    }
    //参数是.. 返回上层，不准在根下使用
    if(args[1][0] == '.'&&args[1][1] == '.')
    {
        if(strcmp(puser->pwd,"/") == 0)
        {
            printf("cd faile! permission deny\n");
            return;
        }
        else
        {
            int tmp = strlen(puser->pwd) - 1;
            while(puser->pwd[tmp]!='/')
            {
                puser->pwd[tmp] = 0;
                tmp--;
            }
            if(tmp!=0)
                puser->pwd[tmp] = 0;
            return;
        }
    }
    //cd+参数： 与服务器通信，核实cd的位置
    //发送请求报文  ：(cmdtype )+ (dataLen + pwd)
    int cmdtype = CMD_CD;
    train_t sender;
    
    //发送命令类型代号  
    ret = send(puser->soecktFd, &cmdtype, sizeof(int), 0);
    if(ret == -1)
    {
        perror("send1 in cmd_cd");
        return;
    }
    printf("ret :%d send cmdtype ok\n", ret);
    
    //接下来是与server cmd_cd函数的通信
    
    //根据args1 格式，处理出完整路径

    if(args[1][0] == '/') //从家目录开始的 绝对路径，路径中不可以有.. 直接传参数过去服务器
    {
        for (int i = 0; args[1][i]&&i<ARGS_LENGTH;++i)
        {
            if(args[1][i] == '.')
            {
                printf("err in cd: '.' and '..' is not allowed\n"); 
                return;
            }    
        }
        strcpy(sender.buf, args[1]);
        sender.dataLen = strlen(args[1]);
        printf("sender.buf: %s\n", sender.buf);
    }
    else//把下层文件夹名拼接后传到服务器，服务器验证是否为可用文件夹
    {
        //这里区分一下是否在根目录，否则出来的格式不对（出现两个/）
        if(strcmp(puser->pwd,"/") == 0)
        {
            sprintf(sender.buf, "%s%s", puser->pwd, args[1]);
        }
        else
        {
        sprintf(sender.buf, "%s/%s", puser->pwd, args[1]);
        }
        sender.dataLen = strlen(sender.buf);
        printf("sender.buf: %s\n", sender.buf);
    }

    //发送cd目标目录的从家开始的 完整路径
    ret = send(puser->soecktFd, &sender, sizeof(int)+sender.dataLen, 0);
    if(ret == -1)
    {
        perror("send2 in cmd_ls");
        return;
    }

    //接受服务器回传消息，确认cd成功
    recvCyc(puser->soecktFd, &ret, sizeof(int));
    if(!ret)
        {
            printf("cd faile! no such dir\n");
            return;
        }
    else
    {
        sprintf(puser->pwd, "%s", sender.buf);
        printf("cd success!\n");
    }
}

//传输功能不支持文件夹，只能传输文件

void cmd_puts(int argc,char args[][ARGS_LENGTH],puserStat puser)
{
    printf("puts runing...\n");
    //检查参数: 0 puts 1 文件名
    if(argc != 2)
    {
        fprintf(stderr, "err in cmd_puts:arg num !\n");
        return;
    }
    //查看本地文件stat
    struct stat statBuf;
    int ret = stat(args[1], &statBuf);
    if(ret == -1)
    {
        perror("stat in cmd_puts");
        return;
    }

    //发送cmdtype

    int cmdtype = CMD_PUTS;

    //发送命令类型代号
    ret = send(puser->soecktFd, &cmdtype, sizeof(int), 0);
    if(ret == -1)
    {
        perror("send1 in cmd_puts");
        return;
    }

    //发送参数
    train_t sender;
    //发送文件名,这里发送的文件名是处理过的，把包含本地路径的文件名处理成pwd路径的文件名再传送给服务器
    char pwdNameBuf[512] = {0};
    int i = 0;
    for (i = strlen(args[1]) - 1; i > 0; --i)
    {
        if(args[1][i] == '/')
            break;
    }
    if(args[1][i] == '/')
        i++;
    for (int j = 0; args[1][i]; ++j)
    {
        pwdNameBuf[j] = args[1][i];
        i++;
    }
        //判断根目录的特殊情况，不然会出现xxx//xxx
    if (strcmp(puser->pwd, "/") == 0)
    {
        sprintf(sender.buf, "%s%s", puser->pwd,pwdNameBuf);
        }
        else
        {
            sprintf(sender.buf, "%s/%s", puser->pwd, pwdNameBuf);
        }
    sender.dataLen = strlen(sender.buf);
    printf("send filename: %s\n", sender.buf);
    ret = send(puser->soecktFd, &sender, sizeof(int) + sender.dataLen, 0);
    if(ret == -1)
    {
        perror("send1 in cmd_puts");
        return;
    }
    //传送文件
        //创建传输连接
    char argv[2][ARGS_LENGTH] = {0};
    ret = read_config(TRANS_NETCONF_FILENAME,argv,2);
    int transFd;
    transFd = socket(AF_INET,SOCK_STREAM,0);
    if(transFd == -1)
    {
        perror("socket in gets");
        return;
    }
    printf("connecting to server...socketFd: %d\n",transFd);
    struct sockaddr_in ser;
    bzero(&ser,sizeof(ser));
    ser.sin_family=AF_INET;
    ser.sin_port=htons(atoi(argv[1]));
    ser.sin_addr.s_addr=inet_addr(argv[0]);
    ret=connect(transFd,(struct sockaddr*)&ser,sizeof(ser));
    if(ret == -1)
    {
        perror("connect in gets");
        return;
    }
    printf("connected , socketFd :%d\n",transFd);
        
        //传输文件

    //发送文件大小 
    bzero(&sender, sizeof(sender));
    memcpy(sender.buf, &statBuf.st_size,sizeof(statBuf.st_size));
    sender.dataLen = sizeof(statBuf.st_size);
    ret = send(transFd, &sender, sizeof(int) + sender.dataLen, 0);
    if(ret == -1)
    {
        perror("send2 in cmd_puts");
        return;
    }

    //mmap
    int fd = open(args[1],O_RDWR);//使用mmap时，记得把这改成可读可写，否则可能会有权限问题
    if(fd == -1)
    {
        perror("open in puts");
        return;
    }
    char *pmap = (char *)mmap(NULL,statBuf.st_size,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
    if(pmap == (char *)-1)
    {
        perror("pmap in puts");
    }
    send(transFd,pmap,statBuf.st_size,0);//这里注意理解mmap 的 零拷贝！

    close(transFd);
    return;
}

//gets是获得当前目录下的一个文件
void cmd_gets(int argc,char args[][ARGS_LENGTH],puserStat puser)
{
    printf("gets runing...\n");
    //检查参数
    if(argc != 2)
    {
        fprintf(stderr, "err in cmd_puts:arg num !\n");
        return;
    }
    //发送cmdtype
    int ret;
    int cmdtype = CMD_GETS;
    
    
    //发送命令类型代号
    ret = send(puser->soecktFd, &cmdtype, sizeof(int), 0);
    if(ret == -1)
    {
        perror("send1 in cmd_puts");
        return;
    }
    //发送参数(要获取的文件名，拼上当前的目录)
    train_t sender;
    bzero(&sender, sizeof(sender));
    if(strcmp(puser->pwd,"/") == 0)
        sprintf(sender.buf, "%s%s", puser->pwd, args[1]);
    else
        sprintf(sender.buf, "%s/%s", puser->pwd, args[1]);
    sender.dataLen = strlen(sender.buf);

    send(puser->soecktFd, &sender, sizeof(int)+sender.dataLen, 0);

    //接收查询结果，是否成功开始传输文件
    int retFlag = 0;
    recvCyc(puser->soecktFd, &retFlag, sizeof(int));
    if(!retFlag)
    {
        fprintf(stderr, "err in cmd_gets :file not exist\n");
        return;
    }
    //建立文件传输专用连接
    char argv[2][ARGS_LENGTH] = {0};
    ret = read_config(TRANS_NETCONF_FILENAME,argv,2);
    int transFd;
    transFd = socket(AF_INET,SOCK_STREAM,0);
    if(transFd == -1)
    {
        perror("socket in gets");
        return;
    }
    printf("connecting to server...socketFd: %d\n",transFd);
    struct sockaddr_in ser;
    bzero(&ser,sizeof(ser));
    ser.sin_family=AF_INET;
    ser.sin_port=htons(atoi(argv[1]));
    ser.sin_addr.s_addr=inet_addr(argv[0]);
    ret=connect(transFd,(struct sockaddr*)&ser,sizeof(ser));
    if(ret == -1)
    {
        perror("connect in gets");
        return;
    }
    printf("connected , socketFd :%d\n",transFd);

    //接收文件
    int dataLen;
    //接收 文件大小
    off_t fileLen = 0;
    recvCyc(transFd,&dataLen,sizeof(int));
    recvCyc(transFd,&fileLen,dataLen);
    printf("recv fileLen: %ld\n",fileLen);
    //创建新文件准备接收

    char downloadPath[128] = "./download/"; //下载到download文件夹里
    strcat(downloadPath, args[1]);

    int fd;
    int i = 1;
    int buflen = strlen(downloadPath);
    int tmp = buflen;
    // 这里实现如果有重名的文件，则在新建文件名后边加递增的数字，直到没有重名
    while(-1 == (fd = open(downloadPath,O_RDWR|O_CREAT|O_EXCL,0666)))
    {
        tmp = buflen;
        while(downloadPath[tmp])
        {
            downloadPath[tmp++] = 0;
        }
        sprintf(downloadPath,"%s%d",downloadPath,i++);
    }


    //接收文件内容
    printf("Receiving file...name :%s ,size : %ld M\n",args[1],fileLen/(1024*1024));
    //增加客户端计时
    struct timeval st,end;
    gettimeofday(&st,NULL);
    //借助mmap，利用DMA 加快文件下载速度
    //
    //mmap前，先ftruncate改变文件大小
    ftruncate(fd,fileLen);
    char *pmap = (char *)mmap(NULL,fileLen,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
    if(pmap == (char*)-1)
    {
        fprintf(stderr, "err in gets :pmap failed\n");
    }
    ret = recvCyc(transFd,pmap,fileLen);
    
    if(!ret)
    {
        gettimeofday(&end,NULL);
        printf("download success ,time use: %.2lf s\n",\
               (double)((end.tv_sec-st.tv_sec)*1000000+end.tv_usec-st.tv_usec)/1000000);
    }
    else
    {
        printf("download failed!\n");
    }
    munmap(pmap,fileLen);
    close(transFd);
    return;
}

